package com.rengwuxian.coursecoroutines._4_flow

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlin.coroutines.EmptyCoroutineContext

/**
 * Flow的功能定位
 * Sequence 、Flow
 *
 * Channel是跨协程传递数据的工具，是个底层工具，不是直接拿来用的。尤其是Flow的API逐步完善后，Channel不适合用于上层功能性工具来调用了。
 * Flow: 事件流、数据流（关注的是同一个协程发送的数据序列，这就是数据流）
 * SharedFlow：从数据流转变为 事件流。关注的是事件序列向多个订阅者的、一对多的、跨协程的通知。
 * StateFlow: 是一种特殊的SharedFlow，提供的是一种状态的订阅。（非常常见）
 *
 * Flow的定位：是一种协程版的Sequence。
 * Sequence:序列的意思。
 * 队列的概念：先进先出。只要具备从队尾插入数据、队头取出数据这两个基本功能，就是队列。比如Java:LinkedList(实现了Deque)
 * 在Java中只要实现了Queue接口，就可以被称为队列。不管你还有没有其他额外的功能。
 *
 * Sequence就是一种按我们提供数据来取数据的功能，但跟队列的区别在于不是数据结构，而是一种机制。提供一种边消费边生产的数据序列。
 * 提供的是数据的规则，而不是数据本身。动态产生数据的。用一条生产一条。
 * 生产：
 *   val nums = sequence {
 *     yield(1)
 *   }
 *   跟协程中的yield()和线程中的yield()完全没有关系,主动让出当前时间片。
 * 使用：
 * for(num in nums){
 *     println(num)
 * }
 * kotlin中有类似的buildList用来创建数据列表。--》刚开始就加好了数据
 * sequence {} 是惰性的lazy，是消极的，装的是数据生产规则，用的时候才去生产。--》不会生产多余的数据，可以提前结束收集。
 * 虽然生产是消极的，但是可能减少生产的总耗时时间。
 * 所以可以在sequence里面使用for循环来生产数据。
 *
 * 可以调用list的asSequence(),本质上是使用list提供了预先准备好的生产数据，而不是提供sequence的内部数据。sequence只有生产规则，没有内部数据。
 * list.asSequence()
 *
 * Sequence适用于有一条数据就赶紧处理，而不是只有把数据准备完成，才去处理的业务场景。
 * SequenceScope上有一个RestrictsSuspension的注解，如果调用挂起函数，必须是它自己的挂起函数才行。--》提供了一种场景化的特权。是用来方便造轮子的。
 * 故sequence内部调用的挂起函数只能是yield()和yieldAll()-->生产多条数据的。
 * Sequence的内部实现，选用的是协程。--》只是让Sequence里的协程逻辑独立运行，而不让跟开发者的协程发生关系。
 * Sequence有多处使用的示例：
 *   val nums = sequence {
 *     yield(1)
 *     yield(2)
 *   }.map { "number $it" }
 *   for (num in nums) {
 *     println("1-- $num")
 *   }
 *
 *   for (num2 in nums) {
 *     println("1--$num2")
 *   }
 * 输出如下：
 * 1-- number 1
 * 1-- number 2
 * 2--number 1
 * 2--number 2
 *
 * 那既然sequence{}的大括号里不能使用挂起函数，如果我想用挂起函数，应该怎么办呢？
 * 答案：用flow
 * val numsFlow = flow {
 *     while (true) {
 *       emit(getData())
 *     }
 *  }
 *  协程版的sequence，需要在协程里使用flow的。
 *  scope.launch {
 *     numsFlow.collect {
 *       println(it)
 *     }
 *   }
 * flow提供一个支持挂起函数的数据流。
 */
fun main() = runBlocking<Unit> {
  val list = buildList {
//    while (true) {
      add(getData())
//    }
  }
  list.asSequence()
  for (num in list) {
    println("List item: $num")
  }
  sequence {
    yield(1)
  }
  val nums = sequence {
    while (true) {
      yield(1)
    }
  }.map { "number $it" }
  for (num in nums) {
    println("1-- $num")
  }

  val numsFlow = flow {
    while (true) {
      emit(getData())
    }
  }.map { "number $it" }
  val scope = CoroutineScope(EmptyCoroutineContext)
  scope.launch {
    numsFlow.collect {
      println(it)
    }
  }
  delay(10000)
}

suspend fun getData(): Int {
  return 1
}
